package com.sinohb.autonomousdriving_test;

import android.annotation.SuppressLint;

import android.app.Activity;
import android.graphics.Color;
import android.graphics.PointF;
import android.os.Build;
import android.os.Looper;
import android.os.Process;
import android.util.Log;
import android.view.View;
import android.view.Window;
import android.view.WindowInsets;
import android.view.WindowInsetsController;
import android.view.WindowManager;
import android.widget.ImageButton;
import android.widget.TextView;

import com.hb.sq3d.Viewer3D;
import com.hb.sq3d.vos.LineArg;
import com.hb.sq3d.vos.ModelArg;
import com.hb.sq3d.vos.Status;
import com.sinohb.autonomousdriving_test.comutil.AsyncThread;
import com.sinohb.autonomousdriving_test.comutil.ComUtil;

import java.util.ArrayList;
import java.util.List;

public class TestCase {
    private final Activity m_activity;
    private final TextView m_titleTv;
    private final TextView m_descTv;
    private final ImageButton m_modeBtn;
    private static final int Invalid = Integer.MAX_VALUE;
    private int m_frameCnt = 0;

    private Status m_status;
    private PointF m_carPos, m_truckPos, m_motoPos;
    private float m_C0_Offset = 0;

    static class ShanQiWellFormattedData {
        static boolean yesSimulating = true;
        static int SendIntervalMs = 16;
    }

    static class MyServer {
        static boolean yesSimulating = false;
        static int LocalPORT = 12345;
    }

    // debug Qt through Android in Android-Qt dual-system
    static class DestServer {
        static boolean yesSimulating = false;
        static String IP = "172.20.1.1";
        static int PORT = 12388;
        static String ip() { return yesSimulating ? IP : null; }
        static int port() { return yesSimulating ? PORT : 0; }
    }

    public TestCase(Activity activity) {
        m_activity = activity;
        m_titleTv = activity.findViewById(R.id.title);
        m_descTv = activity.findViewById(R.id.description);
        m_modeBtn = activity.findViewById(R.id.mode);
        hideWindowInsets();

        if (Viewer3D.NativeReleased)
            return;

        if (ShanQiWellFormattedData.yesSimulating) {
            m_carPos = new PointF(-4.0f, 15.0f);
            m_truckPos = new PointF(-3.5f, 35.0f);
            m_motoPos = new PointF(-3.0f, 20);
            m_status = new Status();
            startSendingDatas(0);
        }

        if (MyServer.yesSimulating)
            runTestReceivingSignals();
    }

    private void startSendingDatas(long delayMs) {
        AsyncThread.postDelayed(new Runnable() {
            @Override
            public void run() {
                ++m_frameCnt;
                testSendingPOJO();
//                displayFeatures();
                AsyncThread.postDelayed(this, ShanQiWellFormattedData.SendIntervalMs);
            }
        }, delayMs);
    }

    private void testSendingPOJO() {
        /** standalone status */
        m_status.zhicheSpeed = 30;
//        m_status.noa = 2;
//        m_status.alc = 4;
//        m_status.zhicheAngle = 1;
        Viewer3D.setComingStatus(m_status);

        List<LineArg> lines = createLineArgs();
        Viewer3D.setComingLines(lines, System.currentTimeMillis(), m_frameCnt, "/");

        List<ModelArg> models = createModelArgs();
        Viewer3D.setComingModels(models, System.currentTimeMillis(), m_frameCnt);
    }

    private List<LineArg> createLineArgs() {
        List<LineArg> lines = new ArrayList<>();
//        lines.addAll(createPointsLines());
        lines.addAll(createEquationLines());
        return lines;
    }

    private List<LineArg> createPointsLines() {
        List<LineArg> lines = new ArrayList<>();

        List<PointF> points = new ArrayList<>();
//        points.add(new PointF(-19.15f,-22f));points.add(new PointF(-19.15f,7.25f));points.add(new PointF(-19f,19f));points.add(new PointF(-18.9f,21.75f));points.add(new PointF(-18.75f,33.15f));points.add(new PointF(-18.6f,44.65f));points.add(new PointF(-18.55f,48.65f));points.add(new PointF(-18.55f,50f));points.add(new PointF(-18.45f,54.15f));points.add(new PointF(-18.15f,74.1f));points.add(new PointF(-17.85f,99.15f));points.add(new PointF(-17.85f,99.9f));points.add(new PointF(-17.65f,102.55f));
//        lines.add(new LineArg(1, 1, 1, points));
//
//        points = new ArrayList<>();
//        points.add(new PointF(-16.1f,-12f));points.add(new PointF(-16.1f,7.2f));points.add(new PointF(-15.7f,33.1f));points.add(new PointF(-15.5f,49.95f));points.add(new PointF(-15.4f,54.15f));points.add(new PointF(-15.05f,74.05f));points.add(new PointF(-14.7f,97.2f));points.add(new PointF(-14.35f,119.45f));
//        lines.add(new LineArg(1, 1, 1, points));
//
//        points = new ArrayList<>();
//        points.add(new PointF(-11.45f,-12f));points.add(new PointF(-11.45f,7.1f));points.add(new PointF(-11.25f,18.8f));points.add(new PointF(-11.15f,31.1f));points.add(new PointF(-11.05f,43.35f));points.add(new PointF(-11f,54.2f));points.add(new PointF(-10.95f,60.95f));points.add(new PointF(-10.8f,85f));points.add(new PointF(-10.6f,115f));
//        lines.add(new LineArg(1, 1, 1, points));
//
//        points = new ArrayList<>();
//        points.add(new PointF(-5.45f,-12f));points.add(new PointF(-5.45f,-10.55f));points.add(new PointF(-5.5f,-5.4f));points.add(new PointF(-5.55f,-0.5f));points.add(new PointF(-5.55f,5.05f));points.add(new PointF(-5.7f,7.1f));points.add(new PointF(-5.8f,31.5f));points.add(new PointF(-5.9f,54.2f));points.add(new PointF(-5.95f,62.45f));points.add(new PointF(-5.95f,63.45f));points.add(new PointF(-6.05f,80.35f));points.add(new PointF(-6.1f,91.6f));points.add(new PointF(-6.15f,108.15f));
//        lines.add(new LineArg(1, 1, 1, points));

        // Test one line
        points = new ArrayList<>();
        points.add(new PointF(7f,-12));points.add(new PointF(-50f,120f));
        //points.add(new PointF(-1.7f,2.5f));points.add(new PointF(-1.75f,7.1f));points.add(new PointF(-1.75f,17.7f));points.add(new PointF(-1.85f,32.9f));points.add(new PointF(-1.9f,48.1f));points.add(new PointF(-1.95f,54.2f));points.add(new PointF(-2f,63.3f));points.add(new PointF(-2f,69.05f));points.add(new PointF(-2.05f,78.45f));points.add(new PointF(-2.15f,93.7f));points.add(new PointF(-2.2f,108.85f));
        lines.add(new LineArg(1, 1, 1, points));

//        points = new ArrayList<>();
//        points.add(new PointF(2.05f,-12f));points.add(new PointF(2.05f,7.1f));points.add(new PointF(2.05f,11.5f));points.add(new PointF(1.95f,30.65f));points.add(new PointF(1.9f,38.85f));points.add(new PointF(1.85f,54.25f));points.add(new PointF(1.7f,81.45f));points.add(new PointF(1.6f,100.7f));points.add(new PointF(1.5f,113.7f));points.add(new PointF(1.5f,116.75f));
//        lines.add(new LineArg(1, 1, 1, points));
//
//        points = new ArrayList<>();
//        points.add(new PointF(6.05f,-12f));points.add(new PointF(6.05f,-9.65f));points.add(new PointF(5.95f,4f));points.add(new PointF(5.95f,6.75f));points.add(new PointF(5.9f,9.3f));points.add(new PointF(5.9f,17.4f));points.add(new PointF(5.85f,24.3f));points.add(new PointF(5.85f,29.25f));points.add(new PointF(5.75f,36.7f));points.add(new PointF(5.75f,41.65f));points.add(new PointF(5.7f,46.7f));points.add(new PointF(5.7f,51.75f));points.add(new PointF(5.7f,54.25f));points.add(new PointF(5.7f,56.9f));points.add(new PointF(5.65f,62.1f));points.add(new PointF(5.65f,67.35f));points.add(new PointF(5.6f,74.95f));points.add(new PointF(5.55f,79.65f));points.add(new PointF(5.5f,89.65f));points.add(new PointF(5.5f,94.7f));points.add(new PointF(5.45f,99.85f));points.add(new PointF(5.45f,104.75f));points.add(new PointF(5.4f,109.9f));points.add(new PointF(5.35f,115.05f));points.add(new PointF(5.35f,119.95f));
//        lines.add(new LineArg(1, 1, 1, points));
//
//        points = new ArrayList<>();
//        points.add(new PointF(6.6f,-12f));points.add(new PointF(6.6f,-10.8f));points.add(new PointF(6.6f,-7.75f));points.add(new PointF(6.55f,-4.8f));points.add(new PointF(6.55f,-1.85f));points.add(new PointF(6.55f,1.1f));points.add(new PointF(6.55f,4.15f));points.add(new PointF(6.55f,6.7f));points.add(new PointF(6.5f,6.75f));points.add(new PointF(6.3f,54.25f));points.add(new PointF(6.15f,81.5f));points.add(new PointF(6.05f,97.5f));
//        lines.add(new LineArg(1, 1, 1, points));

        return lines;
    }

    private List<LineArg> createEquationLines() {// for SQ(colour:0=white;1=yellow), for SSQK(colour:1=white;2=yellow)
        List<LineArg> lines = new ArrayList<>();
        lines.add(new LineArg(4, 1, 1, new float[] {12.0f, 0f, 0f, 0f}, -1000, 1000));
        lines.add(new LineArg(5, 4, 1, new float[] {4.0f, 0f, 0f, 0f}, -1000, 1000));
        lines.add(new LineArg(6, 2, 2, new float[] {-4.0f, 0f, 0f, 0f}, -1000, 1000));
        lines.add(new LineArg(7, 1, 2, new float[] {-12.0f, 0f, 0f, 0f}, -1000, 1000));
        return lines;
    }

    private List<ModelArg> createModelArgs() {
        List<ModelArg> models = new ArrayList<>();
        ModelArg model1 = new ModelArg(0, 1, new PointF(0f, 0), 0, 2);//SQ:-1
        ModelArg model2 = new ModelArg(2, 3, new PointF(0f, 3.0f), 0, 0);//SQ:-1
        ModelArg model3 = new ModelArg(3, 5, new PointF(0.0f, 8.0f), 0, 0);//SQ:-1
        models.add(model1);
        models.add(model2);
        models.add(model3);

//        ModelArg model4 = new ModelArg(13, 0, new PointF(-2.85f, -2.2f), 0);
//        ModelArg model5 = new ModelArg(18, 1, new PointF(0.05f, 21.25f), 0);
//        ModelArg model6 = new ModelArg(4, 0, new PointF(2.35f, -2.1f), 0);
//        models.add(model4);
//        models.add(model5);
//        models.add(model6);
        return models;
    }

    public void displayFeatures() {
        List<ModelArg> models = new ArrayList<>();
        ModelArg car = new ModelArg(1, 0, m_carPos, 0);
        ModelArg truck = new ModelArg(2, 1, m_truckPos, 15);
        ModelArg moto = new ModelArg(3, 2, m_motoPos, 0);
        models.add(car);
        models.add(truck);
        models.add(moto);

        List<LineArg> lines = new ArrayList<>();
        LineArg line1 = new LineArg(1, 1, 1, new float[] {m_C0_Offset + 5.0f, 0.0f, -0.0001f, 0.0f}, -1000, 1000);
        LineArg line2 = new LineArg(2, 3, 0, new float[] {m_C0_Offset + 1.9f, 0.0f, -0.0001f, 0.0f}, -1000, 1000);
        LineArg line3 = new LineArg(3, 4, 1, new float[] {m_C0_Offset + -1.9f, 0f, 0f, 0f}, -1000, 1000);
        LineArg line4 = new LineArg(4, 2, 0, new float[] {m_C0_Offset + -5.36f, 0f, 0f, 0f}, -1000, 1000);
        lines.add(line1);
        lines.add(line2);
        lines.add(line3);
        lines.add(line4);

        m_status.noa = 2;// display direct arrow

        int step1FrameCnt = 100;// car move close to my lane
        int step2FrameCnt = 200;// car move away in my lane
        int step3FrameCnt = 300;// car move close in my lane
        int step4FrameCnt = 400;// car move away to left lane
        int step5FrameCnt = 550;// change lane to right
        int step6FrameCnt = 650;// switch to dark mode
        int step7FrameCnt = 800;// a barrier occur

        String title = "", desc = "";
        if (m_frameCnt < step1FrameCnt) {
            title = "直行引导线";
            desc = "识别报文 NOA 功能状态为 2";
            m_carPos.x += 0.04f;
            m_carPos.y -= 0.05f;
            //Log.i("Feature", "car pos : " + car.pos);

            if (m_frameCnt > 50 && m_frameCnt < 80) {
                title = "AEB碰撞报警";
                desc = "识别5：一级报警\n目标黄色";
                m_status.fcw = 5;
            } else if (m_frameCnt >= 80) {
                title = "AEB碰撞报警";
                desc = "识别6或7：二或三级报警\n目标红色";
                m_status.fcw = 6;
            }
        } else if (m_frameCnt <= step2FrameCnt) {
            desc = "小汽车远离...";
            m_carPos.y += 0.1f;
            if (m_frameCnt > step1FrameCnt + 20 && m_frameCnt < step1FrameCnt + 50) {
                desc = "[AEB碰撞报警] \n识别5：一级报警\n目标显示【黄色】";
                m_status.fcw = 5;
            } else if (m_frameCnt >= step1FrameCnt + 50) {
                desc = "[AEB碰撞报警] \n解除";
                m_status.fcw = -100;
            }
        } else if (m_frameCnt <= step3FrameCnt) {
            desc = "靠近小汽车...";
            m_carPos.y -= 0.08f;
            if (m_frameCnt > step2FrameCnt + 50) {
                title = "自适应巡航控制模式";
                desc = "ACCMode信号为2：跟车\n目标显示【蓝色】";
                m_status.acc = 2;
            }
        } else if (m_frameCnt <= step4FrameCnt) {
            desc = "小汽车换道中...";
            m_carPos.x += 0.04f;
            if (m_frameCnt > step3FrameCnt + 50) {
                title = "自适应巡航控制模式";
                desc = "解除";
                m_status.acc = -100;
            }
        } else if (m_frameCnt <= step5FrameCnt) {
            title = "自动换道状态";
            desc = "识别5：右换道\n有虚线框、引导线\n目标道路上水平4m、垂直30m范围内的目标显示【黄色】";
            m_status.alc = 5;
            float moveDelta = 0.024f;
            m_C0_Offset += moveDelta;
            m_carPos.x += moveDelta;
            m_truckPos.x += moveDelta;
            m_motoPos.x += moveDelta;
        } else if (m_frameCnt <= step6FrameCnt) {
            if (m_frameCnt > step5FrameCnt + 50) {
                title = "系统主题";
                desc = "黑暗模式";
                if (m_frameCnt == step5FrameCnt + 51)
                    clickModeBtn();
            }
        } else if (m_frameCnt <= step7FrameCnt) {
            if (m_frameCnt < step6FrameCnt + 50) {
                desc = "摩托车靠近...";
                m_motoPos.y -= 0.3f;
            } else {
                title = "智能避让功能状态";
                desc = "状态信号为 2 左避让\n左侧水平4m、垂直10m范围内的目标显示【黄色】";

                float moveDelta = 0.035f;
                m_C0_Offset -= moveDelta;
                m_carPos.x -= moveDelta;
                m_truckPos.x -= moveDelta;
                m_motoPos.x -= moveDelta;

                m_status.acas = 2;
            }
        } else {
            desc = "结束";
            m_status.fcw = -100;
        }
        if (m_frameCnt > step5FrameCnt)
            m_status.alc = -100;

        setDesc(title, desc);

        Viewer3D.setComingModels(models, System.currentTimeMillis(), 0);

        Viewer3D.setComingLines(lines, System.currentTimeMillis(), 0, "/");

        Viewer3D.setComingStatus(m_status);
    }

    private void runTestReceivingSignals() {
        String wlan0IP = ComUtil.getLocalIP().get("wlan0");
        Log.i("SocketServer", "wlan0 host address = " + wlan0IP);

        m_status = new Status();
        ComUtil.openSocketChannel(MyServer.LocalPORT, DestServer.ip(), DestServer.port(), (feature, valueType, iValue, fValue) -> {
            Log.i("Socket-Recv", "Socket-Recv: feature = " + feature + ", value = " + iValue);
            switch (feature) {
                // SQ ACC
                case 0x10: {
                    switch (iValue) {
                        case 0: m_status.acc = -100; break;
                        case 1: m_status.acc = 2; break;
                    }
                    break;
                }
                // SQ FCW
                case 0x11: {
                    switch (iValue) {
                        case 0: m_status.fcw = -100; break;
                        case 1: m_status.fcw = 5; break;
                        case 2: m_status.fcw = 6; break;// 6 or 7 is the same
                    }
                    break;
                }
                // SQ ALC
                case 0x12: {
                    switch(iValue) {
                        case 0: m_status.alc = -100; break;
                        case 1: m_status.alc = 2; break;
                        case 2: m_status.alc = 4; break;
                        case 3: m_status.alc = 5; break;
                    }
                    break;
                }
                // SQ ACAS
                case 0x13: {
                    break;
                }
                // SQ Zhiche Speed
                case 0x14: {
                    break;
                }
                // SQ Zhiche YawAngle
                case 0x15: {
                    break;
                }
                // SQ DirectArrow
                case 0x16: {
                    break;
                }
                // LDW
                case 0x02: break;
                // ACC_TG
                case 0x04: break;
                // LampBrake
                case 0x05: break;
                // LampTurn
                case 0x06: break;
                default: Log.e("Socket-Recv", "a invalid code received: feature = " + feature); break;
            }
            Viewer3D.setComingStatus(m_status);
        });
    }

    private void hideWindowInsets() {
        Window window = m_activity.getWindow();
        View decorView = window.getDecorView();
        // hide and stretch to status bar
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
            WindowInsetsController controller = decorView.getWindowInsetsController();
            if (controller != null) {
                controller.setSystemBarsAppearance(0, WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS);
                controller.show(WindowInsets.Type.statusBars());
                window.setDecorFitsSystemWindows(false);
            }
        } else {
            decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
        }
        window.setStatusBarColor(Color.TRANSPARENT);

        // hide and stretch to edge(like digging holes)
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
            window.getAttributes().layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
            WindowManager.LayoutParams params = window.getAttributes();
            params.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
            window.setAttributes(params);
        }
    }

    private void setDesc(String title, String desc) {
        AsyncThread.postUI(() -> { m_titleTv.setText(title); m_descTv.setText(desc); });
    }

    private void clickModeBtn() {
        AsyncThread.postUI(() -> { m_modeBtn.callOnClick(); });
    }

    public void killApp() {
        m_activity.finishAffinity();
        Process.killProcess(Process.myPid());
        System.exit(0);
    }

    public void onResume() {
        Log.i("GLRenderer", "onResume");
    }

    public void onPause() {
        Log.i("GLRenderer", "onPause");
    }

    public void onDestroy() {
        Log.e("GLRenderer", "onDestroy");
        killApp();
    }

    /*************************************************************************************************************/
    private static void checkThread() {
        Log.d("ThreadCheck", "current thread id = " + Thread.currentThread().getId());
        Log.d("ThreadCheck", "is Main Thread: " + (Thread.currentThread() == Looper.getMainLooper().getThread()));
    }

    @SuppressLint("DefaultLocale")
    private static String formatTimeInNanos(long timeNanos) {
        long totalMillis = timeNanos / 1_000_000;

        long hours = (totalMillis / 1000 / 60 / 60) % 24;
        long minutes = (totalMillis / 1000 / 60) % 60;
        long seconds = (totalMillis / 1000) % 60;
        long millis = totalMillis % 1000;

        return String.format("%02d:%02d:%02d:%03d", hours, minutes, seconds, millis);
    }
}